package com.tl.satoken.task;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.tl.satoken.domain.entity.RedisPlayer;
import com.tl.satoken.domain.entity.User;
import com.tl.satoken.mapper.HeroMapper;
import com.tl.satoken.mapper.UserMapper;
import com.tl.satoken.netty.WebSocketFrameHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class CoinGenerationTask {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private WebSocketFrameHandler webSocketFrameHandler;
    
    @Autowired
    private UserMapper userMapper;
    
    @Autowired
    private HeroMapper heroMapper;
    
    private static ObjectMapper objectMapper = new ObjectMapper();

    @Scheduled(fixedRate = 60000)
    public void generateCoinsForAllRooms() {
        try {
            log.info("开始执行金币生成任务");
            
            // 先进行结算
            settleGameForAllRooms();
            
            Set<String> roomIds = getAllRoomIds();
            if (roomIds.isEmpty()) {
                log.info("当前没有活跃房间，跳过金币生成");
                return;
            }
            
            for (String roomId : roomIds) {
                generateCoinsForRoom(roomId);
                // 金币生成完成后，广播给房间内所有在线玩家
                broadcastCoinsToRoomPlayers(roomId);
            }
            
            log.info("金币生成任务完成，处理了 {} 个房间", roomIds.size());
        } catch (Exception e) {
            log.error("金币生成任务执行失败", e);
        }
    }

    private void settleGameForAllRooms() {
        try {
            log.info("开始进行游戏结算");
            
            // 获取所有在线玩家
            List<String> allOnlineSessionIds = getAllOnlineSessionIds();
            if (allOnlineSessionIds.isEmpty()) {
                log.info("没有在线玩家，跳过结算");
                return;
            }
            
            // 按房间分组玩家
            Map<String, List<String>> roomPlayersMap = new HashMap<>();
            for (String sessionId : allOnlineSessionIds) {
                String sessionKey = "game:session:" + sessionId;
                RedisPlayer redisPlayer = (RedisPlayer) redisTemplate.opsForValue().get(sessionKey);
                if (redisPlayer != null && redisPlayer.getRoomId() != null) {
                    String roomId = redisPlayer.getRoomId();
                    roomPlayersMap.computeIfAbsent(roomId, k -> new ArrayList<>()).add(sessionId);
                }
            }
            
            // 为每个房间的玩家进行结算
            for (Map.Entry<String, List<String>> entry : roomPlayersMap.entrySet()) {
                String roomId = entry.getKey();
                List<String> sessionIds = entry.getValue();
                settleGameForRoom(roomId, sessionIds);
            }
            
            // 清除Redis中的所有数据
            clearAllRedisData();
            
            log.info("游戏结算完成，处理了 {} 个房间", roomPlayersMap.size());
            
        } catch (Exception e) {
            log.error("游戏结算失败", e);
        }
    }
    
    private void settleGameForRoom(String roomId, List<String> sessionIds) {
        try {
            log.info("开始结算房间 {} 的游戏，玩家数量: {}", roomId, sessionIds.size());
            
            for (String sessionId : sessionIds) {
                String sessionKey = "game:session:" + sessionId;
                RedisPlayer redisPlayer = (RedisPlayer) redisTemplate.opsForValue().get(sessionKey);
                if (redisPlayer != null) {
                    // 计算该玩家抢到的金币总额
                    double totalValue = calculatePlayerCoinValue(roomId, redisPlayer.getHeroId());
                    
                    // 发送游戏结束消息
                    sendCoinEndMessage(sessionId, totalValue);
                    
                    // 更新用户余额
                    updateUserBalance(redisPlayer.getHeroId(), totalValue);
                    
                    log.info("玩家 heroId: {} 结算完成，抢到金币总额: {}", redisPlayer.getHeroId(), totalValue);
                }
            }
            
        } catch (Exception e) {
            log.error("结算房间 {} 游戏失败", roomId, e);
        }
    }
    
    private double calculatePlayerCoinValue(String roomId, Integer heroId) {
        try {
            String roomCoinsKey = "room:coins:" + roomId;
            Set<Object> coinIds = redisTemplate.opsForSet().members(roomCoinsKey);
            
            double totalValue = 0.0;
            if (coinIds != null) {
                for (Object coinId : coinIds) {
                    String coinKey = "coin:" + coinId;
                    Map<Object, Object> coinData = redisTemplate.opsForHash().entries(coinKey);
                    
                    if (!coinData.isEmpty()) {
                        String coinHeroId = (String) coinData.get("heroId");
                        if (String.valueOf(heroId).equals(coinHeroId)) {
                            String moneyStr = (String) coinData.get("money");
                            if (moneyStr != null && !moneyStr.trim().isEmpty()) {
                                try {
                                    totalValue += Double.parseDouble(moneyStr);
                                } catch (NumberFormatException e) {
                                    log.warn("金币 {} 的金额格式错误: {}", coinId, moneyStr);
                                }
                            }
                        }
                    }
                }
            }
            
            return totalValue;
        } catch (Exception e) {
            log.error("计算玩家 heroId: {} 金币总额失败", heroId, e);
            return 0.0;
        }
    }
    
    private void sendCoinEndMessage(String sessionId, double value) {
        try {
            Map<String, Object> coinEndMessage = new HashMap<>();
            coinEndMessage.put("type", "coinEnd");
            coinEndMessage.put("value", value);
            
            String messageJson = objectMapper.writeValueAsString(coinEndMessage);
            
            ChannelHandlerContext ctx = webSocketFrameHandler.getClientContext(sessionId);
            if (ctx != null && ctx.channel().isActive()) {
                ctx.channel().writeAndFlush(new TextWebSocketFrame(messageJson));
                log.debug("向玩家 sessionId: {} 发送游戏结束消息，金额: {}", sessionId, value);
            }
        } catch (Exception e) {
            log.error("发送游戏结束消息失败，sessionId: {}", sessionId, e);
        }
    }
    
    private void updateUserBalance(Integer heroId, double value) {
        try {
            if (value > 0) {
                // 通过heroId查询用户ID
                Long userId = heroMapper.findUserIdByHeroId(heroId);
                if (userId != null) {
                    // 查询用户
                    User user = userMapper.selectById(userId);
                    if (user != null) {
                        // 更新用户余额
                        BigDecimal currentBalance = user.getBalance() != null ? user.getBalance() : BigDecimal.ZERO;
                        BigDecimal addValue = BigDecimal.valueOf(value);
                        BigDecimal newBalance = currentBalance.add(addValue);
                        user.setBalance(newBalance);
                        userMapper.updateById(user);
                        
                        log.info("更新用户 heroId: {} 余额，原余额: {}，增加: {}，新余额: {}", 
                                heroId, currentBalance, addValue, newBalance);
                    } else {
                        log.warn("未找到 userId: {} 对应的用户", userId);
                    }
                } else {
                    log.warn("未找到 heroId: {} 对应的用户ID", heroId);
                }
            }
        } catch (Exception e) {
            log.error("更新用户 heroId: {} 余额失败", heroId, e);
        }
    }
    
    private List<String> getAllOnlineSessionIds() {
        try {
            List<String> onlineSessionIds = new ArrayList<>();
            
            Set<String> sessionKeys = redisTemplate.keys("game:session:*");
            if (sessionKeys != null) {
                for (String sessionKey : sessionKeys) {
                    String sessionId = sessionKey.substring("game:session:".length());
                    onlineSessionIds.add(sessionId);
                }
            }
            
            return onlineSessionIds;
        } catch (Exception e) {
            log.error("获取所有在线玩家失败", e);
            return new ArrayList<>();
        }
    }
    
    private void clearAllRedisData() {
        try {
            // 只清除金币相关的Redis数据
            Set<String> coinKeys = redisTemplate.keys("coin:*");
            if (coinKeys != null && !coinKeys.isEmpty()) {
                redisTemplate.delete(coinKeys);
                log.info("清除了 {} 个金币相关的Redis键", coinKeys.size());
            }
            
            // 清除房间金币数据，但保留房间本身
            Set<String> roomCoinKeys = redisTemplate.keys("room:coins:*");
            if (roomCoinKeys != null && !roomCoinKeys.isEmpty()) {
                redisTemplate.delete(roomCoinKeys);
                log.info("清除了 {} 个房间金币相关的Redis键", roomCoinKeys.size());
            }
            
            Set<String> roomCoinPositionKeys = redisTemplate.keys("room:coin_position:*");
            if (roomCoinPositionKeys != null && !roomCoinPositionKeys.isEmpty()) {
                redisTemplate.delete(roomCoinPositionKeys);
                log.info("清除了 {} 个房间金币位置相关的Redis键", roomCoinPositionKeys.size());
            }
            
        } catch (Exception e) {
            log.error("清除Redis数据失败", e);
        }
    }

    private Set<String> getAllRoomIds() {
        try {
            Set<String> keys = redisTemplate.keys("game:room:*");
            Set<String> roomIds = new HashSet<>();
            
            if (keys != null) {
                for (String key : keys) {
                    if (key.endsWith(":counter")) {
                        continue;
                    }
                    
                    String roomId = key.substring("game:room:".length());
                    if (!"counter".equals(roomId)) {
                        roomIds.add(roomId);
                    }
                }
            }
            
            return roomIds;
        } catch (Exception e) {
            log.error("获取房间ID列表失败", e);
            return new HashSet<>();
        }
    }

    private void generateCoinsForRoom(String roomId) {
        try {
            clearRoomCoins(roomId);
            
            Set<Integer> usedPositions = new HashSet<>();
            
            for (int i = 0; i < 5; i++) {
                String coinId = generateUniqueCoinId();
                int position = generateUniquePosition(usedPositions);
                double money = generateRandomMoney();
                
                createCoin(coinId, money, position, roomId);
                addCoinToRoom(roomId, coinId, position);
                
                usedPositions.add(position);
            }
            
            log.info("为房间 {} 生成了 5 个金币", roomId);
        } catch (Exception e) {
            log.error("为房间 {} 生成金币失败", roomId, e);
        }
    }

    private void clearRoomCoins(String roomId) {
        try {
            String roomCoinsKey = "room:coins:" + roomId;
            String roomCoinPositionKey = "room:coin_position:" + roomId;
            
            Set<Object> existingCoins = redisTemplate.opsForSet().members(roomCoinsKey);
            if (existingCoins != null && !existingCoins.isEmpty()) {
                for (Object coinId : existingCoins) {
                    String coinKey = "coin:" + coinId;
                    redisTemplate.delete(coinKey);
                }
            }
            
            redisTemplate.delete(roomCoinsKey);
            redisTemplate.delete(roomCoinPositionKey);
            
            log.debug("清理房间 {} 的旧金币数据", roomId);
        } catch (Exception e) {
            log.error("清理房间 {} 金币数据失败", roomId, e);
        }
    }

    private String generateUniqueCoinId() {
        return "coin_" + System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8);
    }

    private int generateUniquePosition(Set<Integer> usedPositions) {
        Random random = new Random();
        int position;
        do {
            position = random.nextInt(11);
        } while (usedPositions.contains(position));
        return position;
    }

    private double generateRandomMoney() {
        Random random = new Random();
        return Math.round((5.0 + random.nextDouble() * 20.0) * 100.0) / 100.0;
    }

    private void createCoin(String coinId, double money, int position, String roomId) {
        try {
            String coinKey = "coin:" + coinId;
            Map<String, Object> coinData = new HashMap<>();
            coinData.put("id", coinId);
            coinData.put("money", String.valueOf(money));
            coinData.put("position", String.valueOf(position));
            coinData.put("roomId", roomId);
            coinData.put("heroId", "");
            
            redisTemplate.opsForHash().putAll(coinKey, coinData);
            redisTemplate.expire(coinKey, 1L, TimeUnit.DAYS);
            
            log.debug("创建金币: {} 金额: {} 位置: {} 房间: {}", coinId, money, position, roomId);
        } catch (Exception e) {
            log.error("创建金币 {} 失败", coinId, e);
        }
    }

    private void addCoinToRoom(String roomId, String coinId, int position) {
        try {
            String roomCoinsKey = "room:coins:" + roomId;
            String roomCoinPositionKey = "room:coin_position:" + roomId;
            
            redisTemplate.opsForSet().add(roomCoinsKey, coinId);
            redisTemplate.opsForHash().put(roomCoinPositionKey, coinId, String.valueOf(position));
            
            redisTemplate.expire(roomCoinsKey, 1L, TimeUnit.DAYS);
            redisTemplate.expire(roomCoinPositionKey, 1L, TimeUnit.DAYS);
        } catch (Exception e) {
            log.error("将金币 {} 添加到房间 {} 失败", coinId, roomId, e);
        }
    }
    
    private void broadcastCoinsToRoomPlayers(String roomId) {
        try {
            // 获取房间内所有金币（只包含未被抢走的）
            List<Map<String, Object>> coins = getRoomCoins(roomId);
            
            Map<String, Object> coinStartMessage = new HashMap<>();
            coinStartMessage.put("type", "coinStart");
            coinStartMessage.put("data", coins);
            
            String messageJson = objectMapper.writeValueAsString(coinStartMessage);
            
            // 获取房间内所有在线玩家
            List<String> onlinePlayerSessionIds = getOnlinePlayersInRoom(roomId);
            int broadcastCount = 0;
            
            for (String sessionId : onlinePlayerSessionIds) {
                ChannelHandlerContext ctx = webSocketFrameHandler.getClientContext(sessionId);
                if (ctx != null && ctx.channel().isActive()) {
                    ctx.channel().writeAndFlush(new TextWebSocketFrame(messageJson));
                    broadcastCount++;
                }
            }
            
            log.info("向房间 {} 中的 {} 个在线玩家广播了金币数据，共 {} 个金币", roomId, broadcastCount, coins.size());
            
        } catch (Exception e) {
            log.error("向房间 {} 广播金币数据失败", roomId, e);
        }
    }
    
    private List<Map<String, Object>> getRoomCoins(String roomId) {
        try {
            String roomCoinsKey = "room:coins:" + roomId;
            
            Set<Object> coinIds = redisTemplate.opsForSet().members(roomCoinsKey);
            if (coinIds == null || coinIds.isEmpty()) {
                return new ArrayList<>();
            }
            
            List<Map<String, Object>> coins = new ArrayList<>();
            for (Object coinId : coinIds) {
                String coinKey = "coin:" + coinId;
                Map<Object, Object> coinData = redisTemplate.opsForHash().entries(coinKey);
                
                if (!coinData.isEmpty()) {
                    // 检查heroId字段是否为空（未被抢走）
                    String heroId = (String) coinData.get("heroId");
                    if (heroId == null || heroId.trim().isEmpty()) {
                        Map<String, Object> coin = new HashMap<>();
                        coin.put("id", coinId);
                        coin.put("position", coinData.get("position"));
                        coins.add(coin);
                    }
                }
            }
            
            return coins;
        } catch (Exception e) {
            log.error("获取房间 {} 金币数据失败", roomId, e);
            return new ArrayList<>();
        }
    }
    
    private List<String> getOnlinePlayersInRoom(String roomId) {
        try {
            List<String> onlineSessionIds = new ArrayList<>();
            
            // 获取所有session键
            Set<String> sessionKeys = redisTemplate.keys("game:session:*");
            if (sessionKeys != null) {
                for (String sessionKey : sessionKeys) {
                    // 从sessionKey中提取sessionId
                    String sessionId = sessionKey.substring("game:session:".length());
                    
                    // 获取玩家会话信息
                    RedisPlayer redisPlayer = (RedisPlayer) redisTemplate.opsForValue().get(sessionKey);
                    if (redisPlayer != null && roomId.equals(redisPlayer.getRoomId())) {
                        onlineSessionIds.add(sessionId);
                    }
                }
            }
            
            return onlineSessionIds;
        } catch (Exception e) {
            log.error("获取房间 {} 在线玩家失败", roomId, e);
            return new ArrayList<>();
        }
    }
}
